using System.ComponentModel;
using System.Runtime.Serialization;

using SharpGL.Media3D;
using SharpGL.Enumerations;

namespace SharpGL.OpenGLAttributes
{
    /// <summary>
    /// This class has the light settings.
    /// </summary>
    [TypeConverter(typeof(System.ComponentModel.ExpandableObjectConverter))]
    [DataContract(/*IsReference = true, */Name = "LightingAttributes", Namespace = "SharpGL.SceneGraph")]
    public class LightingAttributes : OpenGLAttributes
    {
        #region Private Consts

        private static readonly AttributeMask attributeMask = AttributeMask.Lighting;

        #endregion Private Consts

        #region Private Fields

        [DataMember()]
        private bool? enable;
        [DataMember()]
        private bool? localViewer;
        [DataMember()]
        private bool? twoSided;
        [DataMember()]
        private GLColor? ambientLight;

        [DataMember()]
        private FaceMode? materialFaceMode;
        [DataMember()]
        private bool? enableColorMaterial;
        [DataMember()]
        private ColorMaterialMode? colorMaterialMode;
        [DataMember()]
        private GLColor? materialAmbient;
        [DataMember()]
        private GLColor? materialDiffuse;
        [DataMember()]
        private GLColor? materialSpecular;
        [DataMember()]
        private GLColor? materialEmission;
        [DataMember()]
        private int? materialShininess;
        [DataMember()]
        private int[] materialColorIndexes;

        #endregion Private Fields

        #region Protected Methods

        protected override void OnDeserializing()
        {
            AttributeFlags = attributeMask;
        }

        #endregion Protected Methods

        #region Public Constructors

        /// <summary>
        /// Initializes a new instance of the <see cref="LightingAttributes"/> class.
        /// </summary>
        public LightingAttributes()
        {
            AttributeFlags = attributeMask;
        }

        #endregion Public Constructors

        #region Public Methods

        /// <summary>
        /// Returns true if any attributes are set.
        /// </summary>
        /// <returns>
        /// True if any attributes are set
        /// </returns>
        public override bool AreAnyAttributesSet()
        {
            return enable.HasValue ||
                localViewer.HasValue ||
                twoSided.HasValue ||
                ambientLight.HasValue ||
                enableColorMaterial.HasValue ||
                materialFaceMode.HasValue ||
                colorMaterialMode.HasValue ||
                materialAmbient.HasValue ||
                materialDiffuse.HasValue ||
                materialSpecular.HasValue ||
                materialEmission.HasValue ||
                materialShininess.HasValue ||
                materialColorIndexes != null;
        }

        /// <summary>
        /// Reset all attributes to unset state.
        /// </summary>
        public override void ResetAllAttributes()
        {
            enable = null;
            localViewer = null;
            twoSided = null;
            ambientLight = null;

            materialFaceMode = null;
            enableColorMaterial = null;
            colorMaterialMode = null;
            materialAmbient = null;
            materialDiffuse = null;
            materialSpecular = null;
            materialEmission = null;
            materialShininess = null;
            materialColorIndexes = null;
        }

        /// <summary>
        /// Sets the attributes.
        /// </summary>
        /// <param name="gl">The OpenGL instance.</param>
        public override void SetAttributes(OpenGL gl)
        {
            if (enable.HasValue) gl.EnableIf(OpenGL.GL_LIGHTING, enable.Value);
            if (localViewer.HasValue) gl.LightModel(LightModelParameter.LocalViewer, localViewer.Value == true ? 1 : 0);
            if (twoSided.HasValue) gl.LightModel(LightModelParameter.TwoSide, twoSided.Value == true ? 1 : 0);
            if (ambientLight.HasValue) gl.LightModel(LightModelParameter.Ambient, ambientLight.Value);

            if (enableColorMaterial.HasValue) gl.EnableIf(OpenGL.GL_COLOR_MATERIAL, enableColorMaterial.Value);
            if (materialFaceMode.HasValue && colorMaterialMode.HasValue) gl.ColorMaterial((uint)materialFaceMode.Value, (uint)colorMaterialMode.Value);
            if (materialFaceMode.HasValue && materialAmbient.HasValue) gl.Material((uint)materialFaceMode.Value, OpenGL.GL_AMBIENT, materialAmbient.Value);
            if (materialFaceMode.HasValue && materialDiffuse.HasValue) gl.Material((uint)materialFaceMode.Value, OpenGL.GL_DIFFUSE, materialDiffuse.Value);
            if (materialFaceMode.HasValue && materialSpecular.HasValue) gl.Material((uint)materialFaceMode.Value, OpenGL.GL_SPECULAR, materialSpecular.Value);
            if (materialFaceMode.HasValue && materialEmission.HasValue) gl.Material((uint)materialFaceMode.Value, OpenGL.GL_EMISSION, materialEmission.Value);
            if (materialFaceMode.HasValue && materialShininess.HasValue) gl.Material((uint)materialFaceMode.Value, OpenGL.GL_SHININESS, materialShininess.Value);
            if (materialFaceMode.HasValue && materialColorIndexes != null) gl.Material((uint)materialFaceMode.Value, OpenGL.GL_COLOR_INDEXES, materialColorIndexes);
        }

        #endregion Public Methods

        #region Public Properties

        [Description("Is lighting enabled in the scene?"), Category("Lighting")]
        public bool? Enable
        {
            get { return enable; }
            set
            {
                if (enable != value)
                {
                    enable = value;

                    NotifyPropertyChanged("Enable");
                }
            }
        }

        [Description("Does the scene get light depending on camera position?"), Category("Lighting")]
        public bool? LocalViewer
        {
            get { return localViewer; }
            set
            {
                if (localViewer != value)
                {
                    localViewer = value;

                    NotifyPropertyChanged("LocalViewer");
                }
            }
        }

        [Description("Are both sides of a polygon lit?"), Category("Lighting")]
        public bool? TwoSided
        {
            get { return twoSided; }
            set
            {
                if (twoSided != value)
                {
                    twoSided = value;

                    NotifyPropertyChanged("TwoSided");
                }
            }
        }

        [Description("The ambient light for the entire scene."), Category("Lighting")]
        public GLColor? AmbientLight
        {
            get { return ambientLight; }
            set
            {
                if (ambientLight != value)
                {
                    ambientLight = value;

                    NotifyPropertyChanged("AmbientLight");
                }
            }
        }

        /// <summary>
        /// Gets or sets the face mode of the current material.
        /// </summary>
        /// <value>
        /// The face mode of the current material.
        /// </value>
        [Description("Material face mode"), Category("Lighting")]
        public FaceMode? MaterialFaceMode
        {
            get { return materialFaceMode; }
            set
            {
                if (materialFaceMode != value)
                {
                    materialFaceMode = value;

                    NotifyPropertyChanged("MaterialFaceMode");
                }
            }
        }

        /// <summary>
        /// Gets or sets the enable color material.
        /// </summary>
        /// <value>
        /// The enable color material.
        /// </value>
        [Description("Is color material enabled in the scene?"), Category("Lighting")]
        public bool? EnableColorMaterial
        {
            get { return enableColorMaterial; }
            set
            {
                if (enableColorMaterial != value)
                {
                    enableColorMaterial = value;

                    NotifyPropertyChanged("EnableColorMaterial");
                }
            }
        }

        /// <summary>
        /// Gets or sets the color material mode.
        /// </summary>
        /// <value>
        /// The color material mode.
        /// </value>
        [Description("Color material mode."), Category("Lighting")]
        public ColorMaterialMode? ColorMaterialMode
        {
            get { return colorMaterialMode; }
            set
            {
                if (colorMaterialMode != value)
                {
                    colorMaterialMode = value;

                    NotifyPropertyChanged("ColorMaterialMode");
                }
            }
        }

        /// <summary>
        /// Gets or sets the ambient color of the current material.
        /// </summary>
        /// <value>
        /// The ambient color of the current material.
        /// </value>
        [Description("Material ambient"), Category("Lighting")]
        public GLColor? MaterialAmbient
        {
            get { return materialAmbient; }
            set
            {
                if (materialAmbient != value)
                {
                    materialAmbient = value;

                    NotifyPropertyChanged("MaterialAmbient");
                }
            }
        }

        /// <summary>
        /// Gets or sets the diffuse color of the current material.
        /// </summary>
        /// <value>
        /// The diffuse color of the current material.
        /// </value>
        [Description("Material diffuse"), Category("Lighting")]
        public GLColor? MaterialDiffuse
        {
            get { return materialDiffuse; }
            set
            {
                if (materialDiffuse != value)
                {
                    materialDiffuse = value;

                    NotifyPropertyChanged("MaterialDiffuse");
                }
            }
        }

        /// <summary>
        /// Gets or sets the specular color of the current material.
        /// </summary>
        /// <value>
        /// The specular color of the current material.
        /// </value>
        [Description("Material specular"), Category("Lighting")]
        public GLColor? MaterialSpecular
        {
            get { return materialSpecular; }
            set
            {
                if (materialSpecular != value)
                {
                    materialSpecular = value;

                    NotifyPropertyChanged("MaterialSpecular");
                }
            }
        }

        /// <summary>
        /// Gets or sets the emissio color of the current material.
        /// </summary>
        /// <value>
        /// The emissio color of the current material.
        /// </value>
        [Description("Material emmission"), Category("Lighting")]
        public GLColor? MaterialEmission
        {
            get { return materialEmission; }
            set
            {
                if (materialEmission != value)
                {
                    materialEmission = value;

                    NotifyPropertyChanged("MaterialEmission");
                }
            }
        }

        /// <summary>
        /// Gets or sets the shininess of the current material.
        /// </summary>
        /// <value>
        /// The shininess of the current material.
        /// </value>
        [Description("Material shininess"), Category("Lighting")]
        public int? MaterialShininess
        {
            get { return materialShininess; }
            set
            {
                if (materialShininess != value)
                {
                    materialShininess = value;

                    NotifyPropertyChanged("MaterialShininess");
                }
            }
        }

        /// <summary>
        /// Gets or sets the color indexes of the current material.
        /// </summary>
        /// <value>
        /// The color indexes of the current material.
        /// </value>
        [Description("Material color indexes"), Category("Lighting")]
        public int[] MaterialColorIndexes
        {
            get { return materialColorIndexes; }
            set
            {
                if (materialColorIndexes != value)
                {
                    materialColorIndexes = value;

                    NotifyPropertyChanged("MaterialColorIndexes");
                }
            }
        }

        #endregion Public Properties
    }
}
